package com.kexio.enterprise.observability.logging.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;

/**
 * 性能监控AOP切面
 * 
 * 监控方法执行性能，统计执行时间、调用次数等指标
 * 
 * @author Kexio
 * @since 1.0.0
 */
@Aspect
@Component
@Order(1) // 高优先级，最早执行
@ConditionalOnProperty(name = "kexio.enterprise.observability.performance.enabled", havingValue = "true", matchIfMissing = true)
public class PerformanceAspect {

    private static final Logger log = LoggerFactory.getLogger(PerformanceAspect.class);

    // 性能统计数据
    private final ConcurrentHashMap<String, PerformanceMetrics> metricsMap = new ConcurrentHashMap<>();
    
    // 慢方法阈值（毫秒）
    private static final long SLOW_METHOD_THRESHOLD = 1000;
    
    // 超慢方法阈值（毫秒）
    private static final long VERY_SLOW_METHOD_THRESHOLD = 5000;

    /**
     * Service层切点
     */
    @Pointcut("@within(org.springframework.stereotype.Service)")
    public void servicePointcut() {
    }

    /**
     * Controller层切点
     */
    @Pointcut("@within(org.springframework.stereotype.Controller) || " +
              "@within(org.springframework.web.bind.annotation.RestController)")
    public void controllerPointcut() {
    }

    /**
     * Repository层切点
     */
    @Pointcut("@within(org.springframework.stereotype.Repository)")
    public void repositoryPointcut() {
    }

    /**
     * 自定义性能监控注解切点
     */
    @Pointcut("@annotation(com.kexio.enterprise.observability.logging.annotation.PerformanceMonitor)")
    public void performanceMonitorPointcut() {
    }

    /**
     * 性能监控环绕通知
     */
    @Around("servicePointcut() || controllerPointcut() || repositoryPointcut() || performanceMonitorPointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 获取方法信息
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        String className = method.getDeclaringClass().getSimpleName();
        String methodName = method.getName();
        String methodKey = className + "." + methodName;

        // 记录开始时间
        long startTime = System.currentTimeMillis();
        long startNanoTime = System.nanoTime();
        
        Throwable exception = null;
        Object result = null;

        try {
            // 执行目标方法
            result = point.proceed();
            return result;
            
        } catch (Throwable e) {
            exception = e;
            throw e;
            
        } finally {
            // 计算执行时间
            long endTime = System.currentTimeMillis();
            long endNanoTime = System.nanoTime();
            long executionTime = endTime - startTime;
            long executionNanoTime = endNanoTime - startNanoTime;

            // 更新性能指标
            updateMetrics(methodKey, executionTime, executionNanoTime, exception == null);

            // 记录性能日志
            logPerformance(methodKey, executionTime, exception != null);

            // 检查是否为慢方法
            checkSlowMethod(methodKey, executionTime, point.getArgs());
        }
    }

    /**
     * 更新性能指标
     */
    private void updateMetrics(String methodKey, long executionTime, long executionNanoTime, boolean success) {
        PerformanceMetrics metrics = metricsMap.computeIfAbsent(methodKey, k -> new PerformanceMetrics());
        
        metrics.totalCalls.increment();
        metrics.totalExecutionTime.add(executionTime);
        metrics.totalExecutionNanoTime.add(executionNanoTime);
        
        if (success) {
            metrics.successCalls.increment();
        } else {
            metrics.failureCalls.increment();
        }
        
        // 更新最小/最大执行时间
        updateMinTime(metrics, executionTime);
        updateMaxTime(metrics, executionTime);
        
        // 统计慢方法
        if (executionTime >= SLOW_METHOD_THRESHOLD) {
            metrics.slowCalls.increment();
        }
        
        if (executionTime >= VERY_SLOW_METHOD_THRESHOLD) {
            metrics.verySlowCalls.increment();
        }
    }

    /**
     * 更新最小执行时间
     */
    private void updateMinTime(PerformanceMetrics metrics, long executionTime) {
        long currentMin = metrics.minExecutionTime.get();
        while (executionTime < currentMin) {
            if (metrics.minExecutionTime.compareAndSet(currentMin, executionTime)) {
                break;
            }
            currentMin = metrics.minExecutionTime.get();
        }
    }

    /**
     * 更新最大执行时间
     */
    private void updateMaxTime(PerformanceMetrics metrics, long executionTime) {
        long currentMax = metrics.maxExecutionTime.get();
        while (executionTime > currentMax) {
            if (metrics.maxExecutionTime.compareAndSet(currentMax, executionTime)) {
                break;
            }
            currentMax = metrics.maxExecutionTime.get();
        }
    }

    /**
     * 记录性能日志
     */
    private void logPerformance(String methodKey, long executionTime, boolean hasException) {
        if (log.isDebugEnabled()) {
            log.debug("Performance - Method: {}, ExecutionTime: {}ms, Exception: {}", 
                     methodKey, executionTime, hasException);
        }
    }

    /**
     * 检查慢方法
     */
    private void checkSlowMethod(String methodKey, long executionTime, Object[] args) {
        if (executionTime >= VERY_SLOW_METHOD_THRESHOLD) {
            log.warn("Very Slow Method Detected - Method: {}, ExecutionTime: {}ms, Args: {}", 
                    methodKey, executionTime, args != null ? args.length : 0);
        } else if (executionTime >= SLOW_METHOD_THRESHOLD) {
            log.info("Slow Method Detected - Method: {}, ExecutionTime: {}ms", 
                    methodKey, executionTime);
        }
    }

    /**
     * 获取性能统计信息
     */
    public PerformanceStatistics getPerformanceStatistics() {
        PerformanceStatistics statistics = new PerformanceStatistics();
        
        metricsMap.forEach((methodKey, metrics) -> {
            MethodPerformanceInfo info = new MethodPerformanceInfo();
            info.setMethodKey(methodKey);
            info.setTotalCalls(metrics.totalCalls.sum());
            info.setSuccessCalls(metrics.successCalls.sum());
            info.setFailureCalls(metrics.failureCalls.sum());
            info.setSlowCalls(metrics.slowCalls.sum());
            info.setVerySlowCalls(metrics.verySlowCalls.sum());
            info.setTotalExecutionTime(metrics.totalExecutionTime.sum());
            info.setMinExecutionTime(metrics.minExecutionTime.get());
            info.setMaxExecutionTime(metrics.maxExecutionTime.get());
            info.setAvgExecutionTime(info.getTotalCalls() > 0 ? 
                (double) info.getTotalExecutionTime() / info.getTotalCalls() : 0);
            
            statistics.addMethodInfo(info);
        });
        
        return statistics;
    }

    /**
     * 重置性能统计
     */
    public void resetStatistics() {
        metricsMap.clear();
        log.info("Performance statistics reset");
    }

    /**
     * 获取指定方法的性能信息
     */
    public MethodPerformanceInfo getMethodPerformance(String methodKey) {
        PerformanceMetrics metrics = metricsMap.get(methodKey);
        if (metrics == null) {
            return null;
        }
        
        MethodPerformanceInfo info = new MethodPerformanceInfo();
        info.setMethodKey(methodKey);
        info.setTotalCalls(metrics.totalCalls.sum());
        info.setSuccessCalls(metrics.successCalls.sum());
        info.setFailureCalls(metrics.failureCalls.sum());
        info.setSlowCalls(metrics.slowCalls.sum());
        info.setVerySlowCalls(metrics.verySlowCalls.sum());
        info.setTotalExecutionTime(metrics.totalExecutionTime.sum());
        info.setMinExecutionTime(metrics.minExecutionTime.get());
        info.setMaxExecutionTime(metrics.maxExecutionTime.get());
        info.setAvgExecutionTime(info.getTotalCalls() > 0 ? 
            (double) info.getTotalExecutionTime() / info.getTotalCalls() : 0);
        
        return info;
    }

    /**
     * 性能指标内部类
     */
    private static class PerformanceMetrics {
        final LongAdder totalCalls = new LongAdder();
        final LongAdder successCalls = new LongAdder();
        final LongAdder failureCalls = new LongAdder();
        final LongAdder slowCalls = new LongAdder();
        final LongAdder verySlowCalls = new LongAdder();
        final LongAdder totalExecutionTime = new LongAdder();
        final LongAdder totalExecutionNanoTime = new LongAdder();
        final AtomicLong minExecutionTime = new AtomicLong(Long.MAX_VALUE);
        final AtomicLong maxExecutionTime = new AtomicLong(0);
    }

    /**
     * 方法性能信息
     */
    public static class MethodPerformanceInfo {
        private String methodKey;
        private long totalCalls;
        private long successCalls;
        private long failureCalls;
        private long slowCalls;
        private long verySlowCalls;
        private long totalExecutionTime;
        private long minExecutionTime;
        private long maxExecutionTime;
        private double avgExecutionTime;

        // Getters and Setters
        public String getMethodKey() { return methodKey; }
        public void setMethodKey(String methodKey) { this.methodKey = methodKey; }
        
        public long getTotalCalls() { return totalCalls; }
        public void setTotalCalls(long totalCalls) { this.totalCalls = totalCalls; }
        
        public long getSuccessCalls() { return successCalls; }
        public void setSuccessCalls(long successCalls) { this.successCalls = successCalls; }
        
        public long getFailureCalls() { return failureCalls; }
        public void setFailureCalls(long failureCalls) { this.failureCalls = failureCalls; }
        
        public long getSlowCalls() { return slowCalls; }
        public void setSlowCalls(long slowCalls) { this.slowCalls = slowCalls; }
        
        public long getVerySlowCalls() { return verySlowCalls; }
        public void setVerySlowCalls(long verySlowCalls) { this.verySlowCalls = verySlowCalls; }
        
        public long getTotalExecutionTime() { return totalExecutionTime; }
        public void setTotalExecutionTime(long totalExecutionTime) { this.totalExecutionTime = totalExecutionTime; }
        
        public long getMinExecutionTime() { return minExecutionTime; }
        public void setMinExecutionTime(long minExecutionTime) { this.minExecutionTime = minExecutionTime; }
        
        public long getMaxExecutionTime() { return maxExecutionTime; }
        public void setMaxExecutionTime(long maxExecutionTime) { this.maxExecutionTime = maxExecutionTime; }
        
        public double getAvgExecutionTime() { return avgExecutionTime; }
        public void setAvgExecutionTime(double avgExecutionTime) { this.avgExecutionTime = avgExecutionTime; }
    }

    /**
     * 性能统计信息
     */
    public static class PerformanceStatistics {
        private java.util.List<MethodPerformanceInfo> methodInfos = new java.util.ArrayList<>();

        public void addMethodInfo(MethodPerformanceInfo info) {
            methodInfos.add(info);
        }

        public java.util.List<MethodPerformanceInfo> getMethodInfos() {
            return methodInfos;
        }

        public long getTotalCalls() {
            return methodInfos.stream().mapToLong(MethodPerformanceInfo::getTotalCalls).sum();
        }

        public long getTotalSlowCalls() {
            return methodInfos.stream().mapToLong(MethodPerformanceInfo::getSlowCalls).sum();
        }

        public double getOverallAvgExecutionTime() {
            long totalCalls = getTotalCalls();
            if (totalCalls == 0) return 0;
            
            long totalTime = methodInfos.stream().mapToLong(MethodPerformanceInfo::getTotalExecutionTime).sum();
            return (double) totalTime / totalCalls;
        }
    }
}
